package com.chagee.storeManagement.web.config;

import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.validation.Validation;
import org.apache.dubbo.validation.Validator;

/**
 * @author: chang.jiawei
 * @date: 2024/04/18
 */
@Activate(group = {CommonConstants.CONSUMER, CommonConstants.PROVIDER}, order = 9999)
public class DubboParamValidFilter implements Filter {

	private Validation validation;

	public void setValidation(Validation validation) {
		this.validation = validation;
	}

	@Override
	public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
		if (validation != null && !invocation.getMethodName().startsWith("$")
			&& ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), VALIDATION_KEY))) {
			try {
				Validator validator = validation.getValidator(invoker.getUrl());
				if (validator != null) {
					validator.validate(invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
				}
			} catch (RpcException e) {
				throw e;
			} catch (ValidationException e) {
				if (e instanceof ConstraintViolationException) {
					ConstraintViolationException ex = (ConstraintViolationException) e;
					return AsyncRpcResult.newDefaultAsyncResult(
						new ValidationException(ex.getConstraintViolations().stream().findFirst().map(ConstraintViolation::getMessage).orElse(StringUtils.EMPTY_STRING)),
						invocation);
				} else {
					return AsyncRpcResult.newDefaultAsyncResult(new ValidationException(e.getMessage()), invocation);
				}
			} catch (Throwable t) {
				return AsyncRpcResult.newDefaultAsyncResult(t, invocation);
			}
		}
		return invoker.invoke(invocation);
	}

}
